package bosc.cn.proxy;

import bosc.cn.controller.dto.RustSignReqDTO;
import bosc.cn.controller.dto.RustSignResDTO;
import bosc.cn.controller.dto.RustVerifyReqDTO;
import bosc.cn.controller.dto.RustVerifyResDTO;
import bosc.cn.service.RustSignService;
import bosc.cn.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import java.io.IOException;
import java.net.*;
import java.util.Enumeration;

/**
 * @author tianshq
 * @date 2025/9/6 22:07
 * @description 内网客户端WebSocket实现
 */
@Component
@ClientEndpoint
@Slf4j
public class InternalClientWebSocket {

    public static final String SIGN = "sign";
    public static final String REGEX = "@!@";
    public static final String VERIFY = "verify";
    public static final String WEBSOCKET_REGEX = "|";
    private Session session;
    private String clientId;
    private String serverUri;
    private volatile boolean connected = false;

    @Value("${tunnel.server.url}")
    private String serverUrl;

    @Value("${tunnel.client.id}")
    private String defaultClientId;

    @Autowired
    private RustSignService rustSignService;

    @PostConstruct
    public void init() {
        try {
            this.clientId = defaultClientId + "-" + getLocalIP();
            this.serverUri = serverUrl + clientId;
            log.info("初始化WebSocket客户端: {}", serverUri);
            connect();
        } catch (Exception e) {
            log.error("初始化WebSocket客户端失败", e);
        }
    }

    public void connect() {
        try {
            log.info("尝试连接到隧道服务器: {}", serverUri);

            // 验证URI格式
            URI uri = URI.create(serverUri);
            log.info("连接URI: {}", uri.toString());

            // 检查服务器是否可达
            String host = uri.getHost();
            int port = uri.getPort();
            if (port == -1) {
                port = (uri.getScheme().equals("wss")) ? 443 : 80;
            }

            log.info("检查服务器可达性: {}:{}", host, port);
            boolean reachable = checkServerReachable(host, port);
            if (!reachable) {
                log.error("服务器不可达: {}:{}", host, port);
                retryConnect();
                return;
            }

            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            // 设置连接配置
            //container.setDefaultMaxSessionIdleTimeout(30000); // 30秒超时
            // 设置永不超时
            container.setDefaultMaxSessionIdleTimeout(-1); // 永不超时
            container.setAsyncSendTimeout(60000); // 60秒发送超时
            session = container.connectToServer(this, uri);
            connected = true;
            log.info("成功连接到隧道服务器: {}", serverUri);
        } catch (Exception e) {
            connected = false;
            log.error("连接隧道服务器失败: {}", serverUri, e);
            log.error("可能的原因:");
            log.error("1. 公网服务器未启动");
            log.error("2. 服务器地址或端口配置错误");
            log.error("3. 网络连接问题");
            log.error("4. 防火墙阻止连接");
            retryConnect();
        }
    }

    private boolean checkServerReachable(String host, int port) {
        try {
            // 尝试建立TCP连接来检查服务器是否可达
            try (Socket socket = new Socket()) {
                socket.connect(new InetSocketAddress(host, port), 5000); // 5秒超时
                return true;
            }
        } catch (Exception e) {
            log.warn("服务器连接检查失败: {}:{}", host, port, e);
            return false;
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        this.connected = true;
        log.info("与隧道服务器连接已建立，Session ID: {}", session.getId());
    }

    @OnMessage
    public void onMessage(String message) {
        // 消息格式: requestId|response_data
        log.info("收到来自隧道服务器的消息: {}", message);
        String[] parts = message.split("\\|", 2);
        if (parts.length < 2) {
            try {
                session.getBasicRemote().sendText("ERROR: 消息格式不正确");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }

        String requestId = parts[0];
        String requestData = parts[1];

        // 约定requestData 格式为JSON 统一处理
        // 格式: requestId前缀|MethodName@!@外网用户session的requestId@!@JSON

        String[] params = requestData.split(REGEX,3);
        if (params.length < 3) {
            log.error("请求数据格式错误");
        }
        if(SIGN.equals(params[0])) {
            String oriRequestId = params[1];

            RustSignResDTO sign = new RustSignResDTO();
            try {
                // todo 处理来自服务器的消息，替换为实际调用底层RUST
                // 反序列化请求参数 JSON -> RustSignReqDTO
                RustSignReqDTO rustSignReqDTO = JsonUtils.fromJson(params[2], RustSignReqDTO.class);
                log.info("收到签名请求参数: {}", rustSignReqDTO);


                Thread.sleep(50000);
//                sign = rustSignService.sign(rustSignReqDTO);

                //sign.setR("successTest");
                //sign.setS("successTest");
                //sign.setAead("key1");
            } catch (Exception e) {
                log.error("获取RustSign签名失败：" + e.getMessage());
            }

            // 拼装响应 requestId|orirequestId@!@res 响应不用指明methodName，requestId固定反给对应方法,补充原请求id给signservice定位响应
            StringBuilder response = new StringBuilder();
            response.append(requestId).append(WEBSOCKET_REGEX)
                    .append(oriRequestId).append(REGEX)
                    .append(JsonUtils.toJson(sign));
            log.info("返回服务端签名结果: {}", response);

            // 返回服务端签名结果
            if (session != null && session.isOpen()) {
                try {
                    session.getBasicRemote().sendText(response.toString());
                } catch (IOException e) {
                    log.error("发送响应消息失败：" + e.getMessage());
                    e.printStackTrace();
                }
            } else {
                log.warn("WebSocket会话已关闭，无法发送响应");
            }
            return;
        }


        if (VERIFY.equals(params[0])) {
            String oriRequestId = params[1];
            RustVerifyResDTO verifyResDTO = new RustVerifyResDTO();
            try {
                // 从请求里解析出验签请求对象 (JSON -> DTO)
                RustVerifyReqDTO rustVerifyReqDTO = JsonUtils.fromJson(params[2], RustVerifyReqDTO.class);
                log.info("收到验签请求参数: {}", rustVerifyReqDTO);

                verifyResDTO.setValid(true);

            } catch (Exception e) {
                log.error("Rust验签失败：", e.getMessage(), e);
                verifyResDTO.setValid(false);
            }
            log.info("返回服务器验签结果: {}", verifyResDTO);
            // 拼装响应 requestId|orirequestId@!@res 响应不用指明methodName，requestId固定反给对应方法,补充原请求id给signservice定位响应

            StringBuilder response = new StringBuilder();
            response.append(requestId).append(WEBSOCKET_REGEX)
                    .append(oriRequestId).append(REGEX)
                    .append(JsonUtils.toJson(verifyResDTO));

            // 返回服务端验签结果
            if (session != null && session.isOpen()) {
                try {
                    session.getBasicRemote().sendText(response.toString());
                } catch (IOException e) {
                    log.error("发送响应消息失败：" + e.getMessage(), e);
                }
            } else {
                log.warn("WebSocket会话已关闭，无法发送响应");
            }
            return;
        }

    }

    @OnClose
    public void onClose() {
        log.info("与隧道服务器连接已关闭");
        this.connected = false;
        this.session = null;
        retryConnect();
    }

    @OnError
    public void onError(Throwable error) {
        log.error("WebSocket连接异常", error);
        this.connected = false;
        this.session = null;
        retryConnect();
    }

    private void retryConnect() {
        log.info("将在5秒后尝试重新连接...");
        new Thread(() -> {
            try {
                Thread.sleep(5000);
                connect();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("重连线程被中断");
            }
        }).start();
    }

    private String getLocalIP() {
        try {
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface networkInterface = interfaces.nextElement();
                if (networkInterface.isLoopback() || networkInterface.isVirtual() || !networkInterface.isUp()) {
                    continue;
                }

                Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress address = addresses.nextElement();
                    if (address instanceof Inet4Address && !address.isLoopbackAddress()) {
                        return address.getHostAddress().replace(".", "-");
                    }
                }
            }
        } catch (Exception e) {
            log.warn("获取本地IP失败，使用默认值", e);
        }
        return "local-" + System.currentTimeMillis() % 1000;
    }

    public boolean isConnected() {
        return connected && session != null && session.isOpen();
    }
}

